#pragma once
#include <iostream>
#include <chrono>
#include "utility/index.hpp"
#include "request_access/index.hpp"
#include "data_access/index.hpp"
#include "components/index.hpp"
#include "actions.hpp"

using namespace std;
using namespace utility;

namespace components2 {
    std::mutex mtx;

    class MaterialProductStorage : public components::EntityRequest {
    public:
        COMPONENT_INSTANCE_FUNCTION(MaterialProductStorage);

        MaterialProductStorage();

        any Delete2();

        any InStock();

        any OutStock();

    private:
        bool updatePurchaseSupplyStatus(const any &purchaseOrderId, const any &supplyOrderId, bool isCancel);
        bool updateSaleUseStatus(const any &saleOrderId, const any &useOrderId, bool isCancel);

        string inOutStorage(const EntityData &dict, bool isCancel);

        bool insertMaterialProductInStockRecord(const any &supplyOrderId, const any& storageId, bool isCancel);
        bool insertMaterialProductOutStockRecord(const any &useOrderId, const any& storageId, bool isCancel);

        bool updateMaterialProductInStockNum(const any &supplyOrderId, bool isCancel);
        bool updateMaterialProductOutStockNum(const any &useOrderId, bool isCancel);

        any insert2();

        any inOutStock(int storageType,const string& remarkName, const string& noExistsMsg, const string& orderPrimaryKey);

        entity_access::EntityType *supplyOrderEntity;
        entity_access::EntityType *materialUseOrderEntity;
    };

    MaterialProductStorage::MaterialProductStorage() {
        SET_METHOD_ACTION(Delete2);
        SET_METHOD_ACTION(InStock);
        SET_METHOD_ACTION(OutStock);
        supplyOrderEntity = entity_access::GetEntityType("SupplyOrder");
        materialUseOrderEntity = entity_access::GetEntityType("MaterialUseOrder");
    }

    std::any MaterialProductStorage::Delete2() {
        lock_guard<std::mutex> lock(mtx);
        EntityData oldEntityData = SelectEntityByPrimaryKey(QueryRequest->PrimaryKeyValue);
        if (oldEntityData.Empty()) Request->GetMessageMap("记录不存在");

        EntityData obj;
        EntityData dict;
        dict["IsDelete"]=1;
        dict["RowVersion"]= Request->GetParameterValue("RowVersion");

        auto msg = CompareVersion(dict, oldEntityData);
        if (!msg.empty()) return Request->GetMessageMap(msg);

        bool blSucceed = true;
        try {
            DataBase->BeginTransaction();

            blSucceed = UpdateEntityByPrimaryKey(QueryRequest->PrimaryKeyValue, dict);

            if(blSucceed) {
                msg = inOutStorage(oldEntityData, true);
                if (!msg.empty()) {
                    obj = Request->GetMessageMap(msg);
                    blSucceed = false;
                }
            }

            if (blSucceed) DataBase->CommitTransaction();
            else DataBase->RollbackTransaction();

            if (obj.Empty())obj = GetSucceedMap(blSucceed);
        } catch (const std::exception &e) {
            DataBase->RollbackTransaction();
            throw MessageException(e.what(),"");
        }

        return obj;
    }

    any MaterialProductStorage::insert2() {
        lock_guard<std::mutex> lock(mtx);

        EntityData obj;
        EntityData dict = Request->RequestEntity.GetEntityData(Entity->Name);

        bool blSucceed = true;
        try {
            DataBase->BeginTransaction();

            blSucceed = InsertEntity(dict);

            if (blSucceed) {
                auto msg = inOutStorage(dict, false);
                if (!msg.empty()) {
                    obj = Request->GetMessageMap(msg);
                    blSucceed = false;
                }
            }

            if (blSucceed) DataBase->CommitTransaction();
            else DataBase->RollbackTransaction();

            if (obj.Empty())obj = GetSucceedMap(blSucceed);
        } catch (const std::exception &e) {
            DataBase->RollbackTransaction();
            throw MessageException(e.what(),"");
        }

        return obj;
    }

    bool MaterialProductStorage::updatePurchaseSupplyStatus(const any &purchaseOrderId, const any &supplyOrderId, bool isCancel) {
        //先更新供应入库状态
        EntityData supply;
        supply["Status"] = isCancel ? 2 : 3;
        bool blSucceed = UpdateEntity(supplyOrderEntity, GetQueryByPrimaryKey(supplyOrderEntity, supplyOrderId), supply);
        if (blSucceed) blSucceed = Actions::UpdatePurchaseStatus(shared_from_this(), purchaseOrderId);
        return blSucceed;
    }

    bool MaterialProductStorage::updateSaleUseStatus(const any &saleOrderId, const any &useOrderId, bool isCancel) {
        //先更新供应入库状态
        EntityData useOrder;
        useOrder["Status"] = isCancel ? 2 : 3;
        bool blSucceed = UpdateEntity(materialUseOrderEntity, GetQueryByPrimaryKey(materialUseOrderEntity, useOrderId), useOrder);
        if (blSucceed) {
            blSucceed = Actions::UpdateSaleStatus(shared_from_this(), saleOrderId);
            if (blSucceed) blSucceed = Actions::UpdateProduceStatus(shared_from_this(), saleOrderId);
        }
        return blSucceed;
    }

    string MaterialProductStorage::inOutStorage(const EntityData &dict, bool isCancel) {
        int storageType = dict.GetInt("StorageType");
        bool blSucceed = true;
        string msg;

        auto storageId = dict.Get("StorageId");

        if (storageType == 1) {
            auto purchaseOrderId = dict.Get("PurchaseOrderId");
            auto supplyOrderId = dict.Get("DataId");
            if (EntityData::IsNull(purchaseOrderId)) {
                auto supplyOrder = SelectEntityByPrimaryKey(supplyOrderEntity, supplyOrderId);
                if (supplyOrder.Empty()) Request->GetMessageMap("供应单不存在");
                purchaseOrderId = supplyOrder.Get("PurchaseOrderId");
            }

            blSucceed = updatePurchaseSupplyStatus(purchaseOrderId, supplyOrderId, isCancel);
            if (!blSucceed)msg = "更新采购供应状态失败";

            if (blSucceed) {
                blSucceed = insertMaterialProductInStockRecord(supplyOrderId, storageId, isCancel);
                if (!blSucceed)msg = "添加原料商品库存记录失败";
            }

            if (blSucceed) {
                blSucceed = updateMaterialProductInStockNum(supplyOrderId, isCancel);
                if (!blSucceed)msg = "更新原料商品库存失败";
            }
        } else {
            auto saleOrderId = dict.Get("SaleOrderId");
            auto useOrderId = dict.Get("DataId");
            if (EntityData::IsNull(saleOrderId)) {
                auto supplyOrder = SelectEntityByPrimaryKey(materialUseOrderEntity, useOrderId);
                if (supplyOrder.Empty()) Request->GetMessageMap("领用单不存在");
                saleOrderId = supplyOrder.Get("SaleOrderId");
            }

            blSucceed = updateSaleUseStatus(saleOrderId, useOrderId, isCancel);
            if (!blSucceed)msg = "更新销售领用状态失败";

            if (blSucceed) {
                blSucceed = insertMaterialProductOutStockRecord(useOrderId, storageId, isCancel);
                if (!blSucceed)msg = "添加原料商品库存记录失败";
            }

            if (blSucceed) {
                blSucceed = updateMaterialProductOutStockNum(useOrderId, isCancel);
                if (!blSucceed)msg = "更新原料商品库存失败";
            }
        }

        return msg;
    }

    bool MaterialProductStorage::updateMaterialProductInStockNum(const any &supplyOrderId, bool isCancel) {
        auto parameters = make_shared<data_access::DataParameters>();
        parameters->Add("@SupplyOrderId", supplyOrderId);

        string sql = "update t_MaterialProduct a inner join t_SupplyOrder_Detail b on a.MaterialProductId=b.MaterialProductId and b.SupplyOrderId=@SupplyOrderId set StockNum=StockNum";
        if (isCancel) sql += "-b.Num";
        else sql += "+b.Num";

        return DataBase->Execute(sql, parameters)>0;
    }

    bool MaterialProductStorage::insertMaterialProductInStockRecord(const any &supplyOrderId, const any& storageId, bool isCancel) {
        string actionName = isCancel ? "采购供应取消入库" : "采购供应入库";
        auto parameters = make_shared<data_access::DataParameters>();
        parameters->Add("@SupplyOrderId", supplyOrderId);
        parameters->Add("@StorageId", storageId);
        parameters->Add("@ActionName", actionName);
        parameters->Add("@CreateUser", Request->OperationUser);

        stringstream sql;
        sql << "insert into t_MaterialProduct_Stock_Record select uuid(),a.MaterialProductId,@StorageId,b.StockNum,";
        sql << (isCancel ? "0-a.Num," : "a.Num,");
        sql << (isCancel ? " b.StockNum-a.Num," : " b.StockNum+a.Num,");
        sql << "@ActionName,@CreateUser,CURRENT_TIMESTAMP,uuid() from t_supplyorder_detail a,t_materialproduct b";
        sql << " where a.SupplyOrderId=@SupplyOrderId and a.MaterialProductId=b.MaterialProductId";

        return DataBase->Execute(sql.str(), parameters)>0;
    }

    bool MaterialProductStorage::updateMaterialProductOutStockNum(const any &useOrderId, bool isCancel) {
        auto parameters = make_shared<data_access::DataParameters>();
        parameters->Add("@UseOrderId", useOrderId);

        string sql = "update t_MaterialProduct a inner join t_MaterialUseOrder_Detail b on a.MaterialProductId=b.MaterialProductId and b.UseOrderId=@UseOrderId set StockNum=StockNum";
        if (isCancel) sql += "+b.Num";
        else sql += "-b.Num";

        return DataBase->Execute(sql, parameters)>0;
    }


    any MaterialProductStorage::InStock() {
        return inOutStock(1, "Remark", "供应单不存在", "PurchaseOrderId");
    }

    any MaterialProductStorage::inOutStock(int storageType,const string& remarkName, const string& noExistsMsg, const string& orderPrimaryKey) {
        EntityData dict = Request->RequestEntity.GetEntityData(Entity->Name);
        EntityData entityData;

        entityData["StorageType"] = storageType;
        entityData["DataId"] = QueryRequest->PrimaryKeyValue;
        entityData["StorageDate"] = dict["StorageDate"];
        entityData["StorageEmployeeId"] = dict["StorageEmployeeId"];
        entityData["CreateUser"]= Request->OperationUser;
        entityData["Remark"] = dict[remarkName];

        auto oldEntityData = SelectEntityByPrimaryKey(storageType == 1 ? supplyOrderEntity : materialUseOrderEntity, QueryRequest->PrimaryKeyValue);
        if (oldEntityData.Empty()) return Request->GetMessageMap(noExistsMsg);

        auto msg = CompareVersion(dict, oldEntityData);
        if (!msg.empty()) return Request->GetMessageMap(msg);

        entityData[orderPrimaryKey] = oldEntityData[orderPrimaryKey];
        Request->RequestEntity[Entity->Name] = entityData;

        return insert2();
    }

    any MaterialProductStorage::OutStock() {
        return inOutStock(2, "OutRemark", "领用单不存在", "SaleOrderId");
    }

    bool MaterialProductStorage::insertMaterialProductOutStockRecord(const any &useOrderId, const any &storageId, bool isCancel) {
        string actionName = isCancel ? "生产领用取消出库" : "生产领用出库";
        auto parameters = make_shared<data_access::DataParameters>();
        parameters->Add("@UseOrderId", useOrderId);
        parameters->Add("@StorageId", storageId);
        parameters->Add("@ActionName", actionName);
        parameters->Add("@CreateUser", Request->OperationUser);

        stringstream sql;
        sql << "insert into t_MaterialProduct_Stock_Record select uuid(),a.MaterialProductId,@StorageId,b.StockNum,";
        sql << (isCancel ? "a.Num," : "0-a.Num,");
        sql << (isCancel ? "b.StockNum+a.Num," : "b.StockNum-a.Num,");
        sql << "@ActionName,@CreateUser,CURRENT_TIMESTAMP,uuid() from t_MaterialUseOrder_detail a,t_materialproduct b";
        sql << " where a.UseOrderId=@UseOrderId and a.MaterialProductId=b.MaterialProductId";

        return DataBase->Execute(sql.str(), parameters)>0;
    }
}